#ifndef __ADPM_USER_H__
#define __ADPM_USER_H__

#ifndef ADPM_STR_LEN
#define ADPM_STR_LEN 64
#endif

enum{ //enum for response use(server,syscall)
  ADPM_success = 0,
  ADPM_type_undefined,
  ADPM_file_nofound,
  ADPM_inval,
  ADPM_other_err,
};

/* **********************************
 * type for syscall request
 * **********************************/

enum{
  ADPM_call_file_id = 1999, //get a file id
  ADPM_call_munmap, //munmap a file
  ADPM_call_make_dirty, //make a range dirty
};
  
//requests
struct adpm_call_file_id{
  char name[ADPM_STR_LEN];
};
struct adpm_call_munmap{
  unsigned long id;
};
struct adpm_call_make_dirty{
  unsigned long id;
  unsigned long vir_start;
  unsigned long len;
};

//responses
struct _adpm_file_id{
  unsigned long id;
};

struct adpm_call_req{
  long type;
  union{
    struct adpm_call_file_id file_id;
    struct adpm_call_munmap munmap;
    struct adpm_call_make_dirty make_dirty;
  };
}__attribute__((aligned(sizeof(long))));
struct adpm_call_res{
  long status;
  union{
    struct _adpm_file_id file_id;
  };
}__attribute__((aligned(sizeof(long))));

#ifdef ADPM_USER
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <string.h>
#include <sys/mman.h>
#include <assert.h>

#define ADPM_SYS_ID 332

struct adpm_mem{
  //public data
  void *addr;
  unsigned long file_id;
  //private data
  long size;
  int fd;
  char name[ADPM_STR_LEN];
};
static inline unsigned long adpm_user_get_file_id(char *name)
{
  struct adpm_call_req req;
  struct adpm_call_res res;
  if(!name || strlen(name) >= ADPM_STR_LEN)
    return 0;
  req.type = ADPM_call_file_id;
  strcpy(req.file_id.name, name);
  if(syscall(ADPM_SYS_ID, (void *)&req, (void *)&res)){
    fprintf(stderr, "syscall err\n");
    return 0;
  }
  if(res.status != ADPM_success){
    fprintf(stderr, "syscall status err\n");
    return 0;
  }
  return res.file_id.id;
}
static inline int adpm_user_munmap(struct adpm_mem *mem){
  struct adpm_call_req req;
  struct adpm_call_res res;
  int ret = 0;
  
  req.type = ADPM_call_munmap;
  if(!mem->file_id)
    return -1;
  req.munmap.id = mem->file_id;
  if(syscall(ADPM_SYS_ID, (void *)&req, (void *)&res) || res.status != ADPM_success){
    ret = -1;
  }
  munmap(mem->addr, mem->size);
  return ret;
}
static inline char *adpm_user_split_filename(char *src)
{
  int l;
  char *p;
  p = strrchr(src, '/');
  l = strlen(src);
  if(p==NULL)p = src;
  else ++p;
  return p;
}
static inline void *adpm_user_get_mempool(char *name, long size)
{
  void *addr;
  int fd;
  unsigned long id;
  struct adpm_mem *mem;
  char *sn;

  fd = open(name, O_RDWR | O_CREAT);
  if(fd < 0){
    fprintf(stderr, "open %s failed\n", name);
    return NULL;
  }
  addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
  if((unsigned long)addr == (unsigned long) -1){
    close(fd);
    fprintf(stderr, "mmap failed %s\n", name);
    return NULL;
  }  
  sn = adpm_user_split_filename(name);
  id = adpm_user_get_file_id(sn);
  assert(id);
  mem = (struct adpm_mem *)malloc(sizeof(struct adpm_mem));
  assert(mem);
  mem->addr = addr;
  mem->file_id = id;
  mem->fd = fd;
  mem->size = size;
  strcpy(mem->name, sn);
  return mem;
}
static inline int adpm_user_put_mempool(struct adpm_mem *mem){
  int ret = 0;
  
  if(!mem)
    return -1;
  ret = adpm_user_munmap(mem);
  close(mem->fd);
  free(mem);
  if(ret){
    fprintf(stderr, "put mempool wrong\n");
  }
  return ret;
}
static inline int adpm_user_make_dirty(struct adpm_mem* mem, void *addr, long size)
{
  struct adpm_call_req req;
  struct adpm_call_res res;
  req.type = ADPM_call_make_dirty;
  req.make_dirty.id = mem->file_id;
  req.make_dirty.vir_start = (unsigned long)addr;
  req.make_dirty.len = size;
  if(syscall(ADPM_SYS_ID, (void *)&req, (void *)&res)){
    fprintf(stderr, "syscall err\n");
    return -1;
  }
  if(res.status != ADPM_success){
    fprintf(stderr, "syscall status err\n");
    return -1;
  }
  return 0;
}
#endif

#endif
